﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using nl.tno.ict.multitouch.wpfmultitouch.controls;
using nl.tno.ict.multitouch.wpfmultitouch;
using System.Timers;
using System.Windows.Threading;


namespace nl.tno.ict.multitouch.wpfmultitouch.controls
{

    public class SlideList : ListBox
    {

        
        public ScrollMode ScrollMode
        {
            get { return (ScrollMode)GetValue(ScrollModeProperty); }
            set { SetValue(ScrollModeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ScrollMode.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScrollModeProperty =
            DependencyProperty.Register("ScrollMode", typeof(ScrollMode), typeof(SlideList), new UIPropertyMetadata(ScrollMode.scroll));

        public Orientation SlideOrientation
        {
            get { return (Orientation)GetValue(SlideOrientationProperty); }
            set { SetValue(SlideOrientationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SlideOrientation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SlideOrientationProperty =
            DependencyProperty.Register("SlideOrientation", typeof(Orientation), typeof(SlideList), new UIPropertyMetadata(Orientation.Vertical));


        /// <summary>
        /// ForceSelection ensures that there is always a selected item
        /// </summary>
        public bool ForceSelection
        {
            get { return (bool)GetValue(ForceSelectionProperty); }
            set { SetValue(ForceSelectionProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ForceSelection.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ForceSelectionProperty =
            DependencyProperty.Register("ForceSelection", typeof(bool), typeof(SlideList), new UIPropertyMetadata(false));

        public event EventHandler Selected;


        private TouchManager tmc;
        public TouchManager Tmc
        {
            get { return tmc; }
            set { tmc = value;
            Tmc.NewTouch += new Events.TouchEventHandler(tmc_NewTouch);
            Tmc.UpdateTouch += new Events.TouchEventHandler(tmc_UpdateTouch);
            Tmc.RemoveTouch += new Events.TouchEventHandler(tmc_RemoveTouch);
            Tmc.DoubleClick += new Events.TouchEventHandler(tmc_DoubleClick);
            }
        }
        private MTProcessing processing;
        private ScrollViewer sv;
        private DispatcherTimer ScrollTimer;
        private Point lastPos;
        

        List<DependencyObject> hitResultsList;

        public event EventHandler NoTouch;
        public event EventHandler LastTouchRemoved;
        
        public class SelectedEventArgs { int index; object ii; }

        public delegate void SelectedEventHandler(object sender, SelectedEventArgs args);
        public event SelectedEventHandler DoubleSelection;
        
        public bool Stopping = false;
        public bool IsScrolling = false;
        Point lastScrollPos;
        public Point LastSelectedPoint;
        double offset = 0;
        Point LastPos;
        Border scroll_border;
        List<int> SelectedIndexes = new List<int>();
        public int LastHit;

        public event EventHandler Scrolling;

        
        public SlideList()
        {
            MTProperties.SetTouchEnabled(this, true);
            this.Loaded += new RoutedEventHandler(MenuControl_Loaded);
        }


        void MenuControl_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                sv = FindScroll();
                processing = MTProcessing.GetInstance();
                Tmc = processing.getTouchManager(this);

                if (Tmc != null)
                {
                    

                    if (ScrollMode == ScrollMode.scroll)
                    {
                        ScrollTimer = new DispatcherTimer();
                        ScrollTimer.Interval = new TimeSpan(0, 0, 0, 0, 100);
                        ScrollTimer.Tick += new EventHandler(ScrollTimer_Tick);
                    }
                    scroll_border = VisualTreeHelper.GetChild(this, 0) as Border;
                }
                else
                {
                    processing.Log("Error creating SlideList, no touch manager" );
                }
            }
            catch (Exception es) 
            {
                processing.Log("Error creating SlideList : " + es.Message);
            }

        }

        void tmc_DoubleClick(object sender, Events.TouchEventArgs obj)
        {
            int i = CheckHit(obj.t.Finger.CurrentPosition);
            if (DoubleSelection != null)
            {
                DoubleSelection(this, null);
            }

        }

        bool finish;

        void ScrollTimer_Tick(object sender, EventArgs e)
        {
            if (sv != null)
            {
                if (SlideOrientation == Orientation.Vertical)
                {
                    if (sv.Margin.Top > 0 && Tmc.FingerCount == 0)
                    {
                        if (sv.Margin.Top > 20) offset = -(sv.Margin.Top / 2);
                        else { finish = true; offset = -sv.Margin.Top; }
                    }
                    else if (Stopping)
                    {
                        offset = offset / 1.2;
                    }
                    else
                    {
                        offset = lastPos.Y - lastScrollPos.Y;
                    }

                    if (Math.Abs(offset) > 5 || finish)
                    {
                        if (!IsScrolling && Scrolling != null) Scrolling(this, null);
                        IsScrolling = true;

                        sv.Margin = new Thickness(0, sv.Margin.Top + offset, 0, 0);
                        double scrollOffset = ((lastScrollPos.Y - lastPos.Y) / sv.ActualHeight) * sv.ExtentHeight;
                        double newScroll = sv.VerticalOffset + scrollOffset;                        

                        if ((this.SelectionMode == SelectionMode.Single) && (!this.ForceSelection)) this.SelectedIndex = -1;

                        if (finish) { Stopping = false; offset = 0.0; IsScrolling = false; finish = false; }
                    }
                    else
                    {
                        IsScrolling = false;
                        Stopping = false;
                    }

                }
                else
                {

                    if (sv.Margin.Left > 0 && Tmc.FingerCount == 0)
                    {
                        if (sv.Margin.Left > 50) offset = -(sv.Margin.Left / 2);
                        else { finish = true; offset = -sv.Margin.Left; }
                    }
                    else if (Stopping)
                    {
                        offset = offset / 1.2;
                    }
                    else
                    {
                        offset = lastPos.X - lastScrollPos.X;
                    }

                    if (Math.Abs(offset) > 10 || finish)
                    {
                        if (!IsScrolling && Scrolling != null) Scrolling(this, null);
                        IsScrolling = true;

                        Border scroll_border = VisualTreeHelper.GetChild(this, 0) as Border;
                        sv.Margin = new Thickness(sv.Margin.Left + offset, 0, 0, 0);

                        //if (this.SelectionMode == SelectionMode.Single) this.SelectedIndex = -1;

                        if (finish) { Stopping = false; offset = 0.0; }
                        //this.InvalidateVisual();
                    }
                    else
                    {
                        IsScrolling = false;
                        Stopping = false;
                    }
                }

                lastScrollPos = lastPos;
            }
        }



        /// <summary>
        /// Finger was removed, hide menu, send event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="obj"></param>
        void tmc_RemoveTouch(object sender, Events.TouchEventArgs obj)
        {
            if (Tmc.FingerCount == 0)
            {
                if (LastTouchRemoved != null) LastTouchRemoved(this, null);
            }

            if (Tmc.FingerCount == 0 && Math.Abs(this.offset)>5)
            {
                Stopping = true;
                if (NoTouch != null) NoTouch(this, null);                
            }

            if (!Stopping)
            {
                switch (this.SelectionMode)
                {
                    case SelectionMode.Single:
                        this.SelectedIndex = CheckHit(new Point(obj.t.Finger.x, obj.t.Finger.y));
                        break;
                    default:
                        int hit = CheckHit(new Point(obj.t.Finger.x, obj.t.Finger.y));
                        
                        if (SelectedIndexes.Contains(hit))
                        {
                            this.SelectedItems.Remove(this.Items[hit]);
                            SelectedIndexes.Remove(hit);
                        }
                        else { if (hit != -1) { this.SelectedItems.Add(this.Items[hit]); SelectedIndexes.Add(hit); } }

                        break;
                }
            }

            
        }



        // Return the result of the hit test to the callback.
        public HitTestResultBehavior MyHitTestResult(HitTestResult result)
        {
            // Add the hit test result to the list that will be processed after the enumeration.
            hitResultsList.Add(result.VisualHit);

            // Set the behavior to return visuals at all z-order levels.
            return HitTestResultBehavior.Continue;
        }

        private List<DependencyObject> GetChildren(DependencyObject dpo)
        {
            List<DependencyObject> result = new List<DependencyObject>();
            if (dpo != null)
            {
                int count = VisualTreeHelper.GetChildrenCount(dpo);
                for (int i = 0; i < count; i++)
                {
                    result.Add(VisualTreeHelper.GetChild(dpo, i));
                }
            }
            return result;
        }

        public HitTestFilterBehavior HitTestFilterFunc(DependencyObject potentialHitTestTarget)
        {
            hitResultsList.Add(potentialHitTestTarget);
            return HitTestFilterBehavior.Continue;
        }


        public ScrollViewer FindScroll()
        {
            this.UpdateLayout();
            if (VisualTreeHelper.GetChildrenCount(this) > 0)
            {
                Border scroll_border = VisualTreeHelper.GetChild(this, 0) as Border;
                if (scroll_border is Border)
                {
                    ScrollViewer scroll = scroll_border.Child as ScrollViewer;
                    if (scroll is ScrollViewer)
                    {
                        return scroll;
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            return null;

        }

        private int CheckHit(Point p)
        {
            // Get relative position of point
            Point refP = MTProcessing.GetInstance().MainWindow.TranslatePoint(p, (UIElement)this.Parent); // ((Visual)this).PointFromScreen(new Point(0, 0));

            int index = -1;
            if (ForceSelection) index = this.SelectedIndex;            
            //if (tmc.FingerCount == 1)
            {
                // do hit test
                hitResultsList = new List<DependencyObject>();
                VisualTreeHelper.HitTest((Visual)this.Parent, HitTestFilterFunc, new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(refP));

                // if object was hit
                if (hitResultsList.Count > 0)
                {
                    // go through all diferent hits
                    foreach (DependencyObject v in hitResultsList)
                    {

                        int i = 0;
                        // check if a menu item was hit
                        var children = GetChildren(sv);
                        if (children.Count > 0)
                        {
                            Grid b = (Grid)children[0];
                            List<DependencyObject> items = GetChildren(GetChildren(GetChildren(GetChildren(b)[1])[0])[0]);
                            //items.RemoveAt(items.Count - 1);
                            foreach (DependencyObject item in items)
                            {
                                if (item == v) { index = i; LastSelectedPoint = ((FrameworkElement)item).PointToScreen(new Point(0, 0)); return index; }
                                i++;
                            }
                        }

                    }
                }

            }
            return index;
        }

        void tmc_UpdateTouch(object sender, Events.TouchEventArgs obj)
        {            
            if (ScrollMode == ScrollMode.selection && Tmc.FingerCount == 1 && !IsScrolling)
            {
                try
                {
                    if (this.SelectionMode == SelectionMode.Single)
                    {
                        int newSelectedIndex = CheckHit(new Point(obj.t.Finger.x, obj.t.Finger.y));
                        if (this.SelectedIndex != newSelectedIndex)
                        {
                            //this.SelectedIndex = -1;
                            this.SelectedIndex = newSelectedIndex;
                        }
                    }
                }
                catch (Exception es)
                {
                    Console.WriteLine(es.Message);
                }
            }

            if (Tmc.FingerCount > 1)
            {
                LastPos = obj.t.Finger.CurrentPosition;                
                // vertical                                
            }
            #region scale
            /*
            if ((tmcSeries.FingerCount>1) && (lastScaleTime.AddSeconds(2) < DateTime.Now))
            {
                if (lastScaleTime.Year > 2000 && !Double.IsInfinity(lastScale) && !Double.IsInfinity(tmcSeries.scale))
                {
                    if ((tmcSeries.scale - lastScale) > 0.2) GoBig();
                    if ((tmcSeries.scale - lastScale) < -0.2) GoSmall();
                } else lastScaleTime = DateTime.Now;
                lastScale = tmcSeries.scale;
            if ((tmMap.FingerCount>1) && (lastScaleTime.AddSeconds(2) < DateTime.Now))
            {
                if (lastScaleTime.Year > 2000 && !Double.IsInfinity(lastScale) && !Double.IsInfinity(tmMap.scale))
                {
                    if ((tmMap.scale - lastScale) > 0.2) GoBig();
                    if ((tmMap.scale - lastScale) < -0.2) GoSmall();
                } else lastScaleTime = DateTime.Now;
                lastScale = tmMap.scale;
            } */
            #endregion

            if (ScrollMode == ScrollMode.scroll)
            {
                lastPos = processing.MainWindow.TranslatePoint(obj.t.Finger.CurrentPosition, this); //obj.t.Finger.RelativePosition;
            }
            //_processing.WriteToDebug(lastPos.ToString());

        }



        void tmc_NewTouch(object sender, Events.TouchEventArgs obj)
        {
           
                if (ScrollMode == ScrollMode.scroll && Tmc.FingerCount > 0 && !ScrollTimer.IsEnabled) ScrollTimer.Start();
                if (Tmc.FingerCount == 1)
                {
                    Stopping = false;
                    if (ScrollMode == ScrollMode.scroll)
                    {
                        lastPos = processing.MainWindow.TranslatePoint(obj.t.Finger.CurrentPosition, this);
                    }
                    lastScrollPos = lastPos;
                    if (false && !IsScrolling)
                    {
                        switch (this.SelectionMode)
                        {
                            case SelectionMode.Single:
                                //int newhit = CheckHit(new Point(obj.t.Finger.x, obj.t.Finger.y));
                                //if (newhit != LastHit) { this.SelectedIndex = newhit; LastHit = newhit; }                            
                                break;
                            case SelectionMode.Multiple:
                                int hit = CheckHit(new Point(obj.t.Finger.x, obj.t.Finger.y));
                                //_processing.WriteToDebug("hit - " + hit.ToString()); //obj.t.Finger.x.ToString() + " - " + obj.t.Finger.y.ToString());

                                if (SelectedIndexes.Contains(hit))
                                {
                                    this.SelectedItems.Remove(this.Items[hit]);
                                    SelectedIndexes.Remove(hit);
                                }
                                else { if (hit != -1) { this.SelectedItems.Add(this.Items[hit]); SelectedIndexes.Add(hit); } }

                                break;
                        }
                    }

                }
                if (Tmc.FingerCount > 1)
                {
                    //LastPos = tmc._centerPoint;
                    //tmMap.c

                }

           
        }



    }



    public enum ScrollMode
    {
        selection,
        scroll
    }

}



